%%html
<style>
div.input {
display:none;
}
</style>
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import os
%matplotlib inline
import seaborn as sns
import plotly.express as px
from plotly.subplots import make_subplots
sns.set(context="notebook", style="whitegrid", palette="deep", font="Arial", font_scale=1, color_codes=True)
from sklearn.linear_model import LinearRegression
from datetime import timedelta
import plotly.graph_objects as go
import scipy.optimize as opt
from statsmodels.api import OLS
from IPython.display import display, HTML
from scipy.optimize import curve_fit
#Read in data. Canada data is from the Canada gov website itself
data_src="https://github.com/CSSEGISandData/COVID-19/raw/master/csse_covid_19_data/csse_covid_19_time_series/"
cfm=pd.read_csv(data_src+"time_series_covid19_confirmed_global.csv")
dt=pd.read_csv(data_src+"time_series_covid19_deaths_global.csv")
can=pd.read_csv("https://health-infobase.canada.ca/src/data/covidLive/covid19.csv")
can=can[can['prname']!='Canada'][['date','prname','numtotal','numdeaths']]
can.date=pd.to_datetime(can.date,format='%d-%m-%Y').dt.strftime('%Y-%m-%d')
can['Country']='Canada'
tmp_cfm=can.pivot_table(values='numtotal',index=['Country','prname'],columns='date')
tmp_dt=can.pivot_table(values='numdeaths',index=['Country','prname'],columns='date')
def df_reshape(df,df_can,var, reglist):
df.rename(columns={'Country/Region':'Country', 'Province/State':'Region'},inplace=True)
df.Region.fillna('',inplace=True)
df=df.drop(columns=['Lat', 'Long']).groupby(['Country','Region']).sum()
df.columns=pd.to_datetime(pd.Series(df.columns),format='%m/%d/%y').dt.strftime('%Y-%m-%d')
maxdate=min(max(df.columns),max(df_can.columns))
df=df[df.columns[df.columns<=maxdate]]
df_can=df_can[df_can.columns[df_can.columns<=maxdate]]
tp=pd.MultiIndex.from_tuples(tuple([['Canada',x] for x in reglist]))
new_can=pd.DataFrame(index=tp,columns=df.columns)
for pr in reglist:
new_can.loc[('Canada',pr),df_can.columns]=df_can.loc[('Canada',pr)]
new_can.fillna(0,inplace=True)
new_can=new_can.cummax(axis=1)
df.drop(index='Canada',inplace=True)
df=df.append(new_can)
idxname=df.index.names
df_new=pd.melt(df.reset_index(),id_vars=idxname,\
value_vars=df.columns,var_name='Date_updated',value_name=var)
df_new[var+'_added']=df_new[var]-df_new.groupby(idxname)[var].shift(1).fillna(0)
df_new['Date_updated']=pd.to_datetime(df_new['Date_updated'])
return(df_new, df)
def gauss_two(x, mu1, sig1, a1, mu2, sig2, a2):
y1=a1*np.exp(-np.power(x - mu1, 2.) / (2 * np.power(sig1, 2.)))
y2=a2*np.exp(-np.power(x - mu2, 2.) / (2 * np.power(sig2, 2.)))
return(y1+y2)
def gauss_one(x, mu, sig, a):
y=a*np.exp(-np.power(x - mu, 2.) / (2 * np.power(sig, 2.)))
return(y)
df, df_wide=df_reshape(cfm,tmp_cfm, var='Confirmed', reglist=can.prname.unique())
df2, df2_wide=df_reshape(dt,tmp_dt, var='Death', reglist=can.prname.unique())
df=df.merge(df2,on=['Country','Region','Date_updated'],how='left')
maxdate=df['Date_updated'].max()
date_max=(maxdate + np.timedelta64(15,'D')).strftime('%Y-%m-%d')
print(f'Data version: {maxdate.strftime("%Y-%m-%d")}')
df_country=df.groupby(['Country','Date_updated'])[['Confirmed','Death','Confirmed_added','Death_added']].sum().reset_index()
df_now=df_country.groupby('Country')[['Confirmed','Death']].max().reset_index()
dfa=df_country[df_country.Country.isin(df_now[df_now.Confirmed > 300]['Country'])].copy()
dfa.loc[:,'StartDate']=-999
for j in dfa.Country.unique():
sdate=dfa.loc[(dfa.Country==j) & (dfa.Confirmed > 100),'Date_updated'].min()
##print(f"Country: {j} case number: {dfa[(dfa.Country==j) & (dfa.Confirmed > 100)]['Confirmed'].min()} start date {sdate}")
tmp=dfa.loc[dfa.Country==j,'Date_updated']-sdate
dfa.loc[dfa.Country==j,'StartDate']=tmp.dt.days + 1
if j == 'China':
dfa.loc[dfa.Country==j,'StartDate']+=5
When aligned by the first day since the 100th case in each country, the total confirmed case number of all the countries grow similarly except for a few: it follows a steep exponential growth at the early stage which is resulted from an unhampered epidemic spread, gradually slows down after various countermeasures are taken by the governments and eventually goes to flat when the situation is under control.
Countries that are successful in the early prevention and control, like Japan and Singapore, show a much shallower growth with time; South Korea experienced a rapid growth at the early stage but managed to flatten out the curve very quick by an aggressive 'trace, test and treat' strategy. However, recently, many countries has experienced a second wave of growth.
Canada case number grows slowly after a huge increase in Spring.
#countrylist=allcountrylist[:10]
#countrylist.append('Japan')
cl=px.colors.qualitative.Dark24
cblue=cl[0]
corange=cl[8]
cgreen=cl[2]
cred=cl[3]
cl2=[x for x in cl if x not in [cblue,corange,cgreen,cred]]
#countrylist.remove('China').remove('Korea, South').remove('Italy')
fig=px.line(log_y=True,range_x=[0,300],range_y=[100,10000000],width=1000, height=600)
#fig=px.line(dfa[dfa.Country.isin(countrylist)],x='StartDate',y='Confirmed',color='Country',log_y=True
# ,range_x=[0,40],range_y=[100,100000])
cty='China'
clr=cblue #blue
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed']
,mode="lines",name=cty,line=dict(width=2, color=clr))
cty='Italy'
clr=corange #Orange
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed']
,mode="lines",name=cty,line=dict(width=2, color=clr))
cty='US'
clr=cgreen #green
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed']
,mode="lines",name=cty,line=dict(width=2,color=clr))
ctryl=['Spain','Germany','Iran','France','Switzerland','United Kingdom','Japan',
'Singapore','Czechia','Denmark','Sweden','Austria','Norway','Brazil','Russia']
for j in range(len(ctryl)):
fig.add_scatter(x=dfa.loc[dfa.Country==ctryl[j],'StartDate'],y=dfa.loc[dfa.Country==ctryl[j],'Confirmed']
,mode="lines",name=ctryl[j],line=dict(width=2,color=cl2[j]))
cty='Korea, South'
clr='yellow'
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed']
,mode="lines",name=cty,line=dict(width=2,color=clr))
cty='Canada'
clr=cred #red
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed']
,mode="lines+markers",name=cty,marker=dict(size=2,color=clr,line=dict(width=4,color=clr)))
fig.update_layout(
title="COVID-19 Cases by Country",title_x=0.5,
xaxis=dict(showline=True,showgrid=True,showticklabels=True,linecolor='black',linewidth=2,
ticks='outside', mirror=True,
gridcolor="rgba(0,0,0,0.2)",
tickfont=dict(
family='Arial',
size=12,
color='black',
)),
yaxis=dict(showline=True,showgrid=True,showticklabels=True,linecolor='black',linewidth=2,
ticks='inside',mirror=True,
gridcolor="rgba(0,0,0,0.2)",
tickfont=dict(
family='Arial',
size=12,
color='black',
)),
plot_bgcolor='white',
xaxis_title="Number of Days since 100th Case",
yaxis_title="Confirmed Case",
font=dict(
family="Arial",
size=14,
color="black"
)
)
fig.show()
#fig.write_image("output/trend_by_country"+ dfa.Date_updated.max().strftime('_%m_%d_%Y')+".jpeg", scale=3)
#countrylist=allcountrylist[:10]
#countrylist.append('Japan')
cl=px.colors.qualitative.Dark24
cblue=cl[0]
corange=cl[8]
cgreen=cl[2]
cred=cl[3]
cl2=[x for x in cl if x not in [cblue,corange,cgreen,cred]]
#countrylist.remove('China').remove('Korea, South').remove('Italy')
fig=px.line(log_y=True,range_x=[0,300],range_y=[1,100000],width=1000, height=600)
#fig=px.line(dfa[dfa.Country.isin(countrylist)],x='StartDate',y='Confirmed',color='Country',log_y=True
# ,range_x=[0,40],range_y=[100,100000])
cty='China'
clr=cblue #blue
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed_added'].rolling(10).mean() + 1e-3
,mode="lines",name=cty,line=dict(width=2, color=clr))
cty='Italy'
clr=corange #Orange
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed_added'].rolling(10).mean() + 1e-3
,mode="lines",name=cty,line=dict(width=2, color=clr))
cty='US'
clr=cgreen #green
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed_added'].rolling(10).mean() + 1e-3
,mode="lines",name=cty,line=dict(width=2,color=clr))
ctryl=['Spain','Germany','Iran','France','Switzerland','United Kingdom','Japan',
'Singapore','Czechia','Denmark','Sweden','Austria','Norway','Brazil','Russia']
for j in range(len(ctryl)):
fig.add_scatter(x=dfa.loc[dfa.Country==ctryl[j],'StartDate'],y=dfa.loc[dfa.Country==ctryl[j],'Confirmed_added'].rolling(10).mean() + 1e-3
,mode="lines",name=ctryl[j],line=dict(width=2,color=cl2[j]))
cty='Korea, South'
clr='yellow'
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed_added'].rolling(10).mean() + 1e-3
,mode="lines",name=cty,line=dict(width=2,color=clr))
cty='Canada'
clr=cred #red
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed_added'].rolling(10).mean() + 1e-3
,mode="lines+markers",name=cty,marker=dict(size=2,color=clr,line=dict(width=4,color=clr)))
fig.update_layout(
title="COVID-19 Cases by Country",title_x=0.5,
xaxis=dict(showline=True,showgrid=True,showticklabels=True,linecolor='black',linewidth=2,
ticks='outside', mirror=True,
gridcolor="rgba(0,0,0,0.2)",
tickfont=dict(
family='Arial',
size=12,
color='black',
)),
yaxis=dict(showline=True,showgrid=True,showticklabels=True,linecolor='black',linewidth=2,
ticks='inside',mirror=True,
gridcolor="rgba(0,0,0,0.2)",
tickfont=dict(
family='Arial',
size=12,
color='black',
)),
plot_bgcolor='white',
xaxis_title="Number of Days since 100th Case",
yaxis_title="Confirmed Case - Daily Adds",
font=dict(
family="Arial",
size=14,
color="black"
)
)
fig.show()
#fig.write_image("output/trend_by_country"+ dfa.Date_updated.max().strftime('_%m_%d_%Y')+".jpeg", scale=3)
s1_cnd=dfa.loc[(dfa.Country=='Canada') & (dfa.Confirmed > 1000),'Date_updated'].min()
pred=pd.DataFrame({'Date_updated':[ s1_cnd + timedelta(days=x-9) for x in range(250)]})
pred=pred.merge(dfa.loc[dfa.Country=='Canada',['Date_updated','Confirmed']], how='left',on='Date_updated')
dcan=dfa.loc[dfa.Country=='Canada',['Date_updated','Confirmed']].tail(5)
x=np.arange(10)
xx=np.array([np.repeat(1,10),x]).transpose()
mod=OLS(np.log(dcan['Confirmed']), xx[:5]).fit()
pdx=mod.get_prediction(xx)
predmean=np.exp(pdx.predicted_mean)
conf=np.exp(pdx.conf_int(alpha=0.05))
out=pd.DataFrame({'Date_updated': dcan['Date_updated'].append(dcan['Date_updated']+timedelta(days=5)),
'predict':predmean,
'cf_low':conf[:,0],'cf_high':conf[:,1]})
pred=pred.merge(out,how='left',on='Date_updated')
pred['Daily Adds']=float('nan')
tmp=pred.Confirmed.iloc[1:(len(pred)-1)].values-pred.Confirmed.iloc[0:(len(pred)-2)].values
pred.iloc[1:(len(pred)-1),5]=tmp.copy()
cty='US'
tmp=dfa.loc[dfa.Country==cty,['Date_updated','Confirmed']]
tmp['Date_updted_'+cty]=tmp['Date_updated']
tmp['Date_updated']=tmp['Date_updated'] - tmp.loc[tmp.Confirmed > 1000, 'Date_updated'].min() + s1_cnd
tmp.rename(columns={"Confirmed": cty + " - Date Adjusted"}, inplace=True)
pred=pred.merge(tmp,how='left', on='Date_updated')
cty='Brazil'
tmp=dfa.loc[dfa.Country==cty,['Date_updated','Confirmed']]
tmp['Date_updted_'+cty]=tmp['Date_updated']
tmp['Date_updated']=tmp['Date_updated'] - tmp.loc[tmp.Confirmed > 1000, 'Date_updated'].min() + s1_cnd
tmp.rename(columns={"Confirmed": cty + " - Date Adjusted"}, inplace=True)
pred=pred.merge(tmp,how='left', on='Date_updated')
cty='Russia'
tmp=dfa.loc[dfa.Country==cty,['Date_updated','Confirmed']]
tmp['Date_updted_'+cty]=tmp['Date_updated']
tmp['Date_updated']=tmp['Date_updated'] - tmp.loc[tmp.Confirmed > 1000, 'Date_updated'].min() + s1_cnd
tmp.rename(columns={"Confirmed": cty + " - Date Adjusted"}, inplace=True)
pred=pred.merge(tmp,how='left', on='Date_updated')
cty='United Kingdom'
tmp=dfa.loc[dfa.Country==cty,['Date_updated','Confirmed']]
tmp['Date_updted_'+cty]=tmp['Date_updated']
tmp['Date_updated']=tmp['Date_updated'] - tmp.loc[tmp.Confirmed > 1000, 'Date_updated'].min() + s1_cnd
tmp.rename(columns={"Confirmed": cty + " - Date Adjusted"}, inplace=True)
pred=pred.merge(tmp,how='left', on='Date_updated')
eve=[['2020-03-13','Recommendation against Intl. travel'],
['2020-03-16','State of Emergency'],
['2020-03-17','Restricted entry into Canada'],
['2020-03-20','US-Canada border closed for non-essential travel'],
['2020-03-24','Quarantine Act invoked'],
['2020-03-30','Domestic travel restriction']]
eve=pd.DataFrame(eve, columns=['Date_updated','events'])
eve.Date_updated=pd.to_datetime(eve.Date_updated)
pred=pred.merge(eve,how='left',on='Date_updated')
pred['predict_adds']=pred['predict']-pred['predict'].shift(1)
#pred.to_csv('output/CanadaGrowth'+ dfa.Date_updated.max().strftime('_%m_%d_%Y')+'.csv')
Since Canada is still at the exponential growth period, an exponential projection provides a good short-term forecast. In the following chart, the next 5 day's case number is projected based on the exponential growth of the previous 5 day's actual case numbers. The different countermeasures taken by the government are timelined on the chart to monitor their impacts on the growth.
After reaching 1000 cases, the growth rate of Canada is actually not very different from the growth rate of many other countries when they had a similar number of cases. The plot below shows the growth curve of Canada vs US, Italy, Korean and China after other countries' dates are backshifted. The trajectories of those countries provide different scenaria on the long term case growth in Canada.
fig=px.line(log_y=True,range_x=['2020-03-08',date_max],range_y=[100,10000000],width=900, height=600)
captext="Canada COVID-19 case growth with time. US, Brazil, Russia and UK's curves are shifted by days to align with<br>"\
+ "the 1000th case day in Canada. Exponential projection of the future 5 days are calculated using the previous 5 day<br>"\
+"numbers. The shaded area indicates the 95% confidence interval of the projection."
cl=['green','orange','gold','blue']
cty=['US', 'Brazil', 'Russia', 'United Kingdom']
lname=['US - date shifted', 'Brazil - date shifted',
'Russia - date shifted','United Kingdom - date shifted']
for i in range(len(cl)):
fig.add_scatter(x=pred['Date_updated'],y=pred[cty[i] +' - Date Adjusted'], mode="lines",
name=lname[i],line=dict(width=3,color=cl[i]))
fig.add_trace(go.Scatter(x=pred['Date_updated'], y=pred['cf_low'],
fill=None, mode='lines',line_color='rgba(0,0,0,0)',showlegend=False))
fig.add_trace(go.Scatter(x=pred['Date_updated'], y=pred['cf_high'],
fill='tonexty',mode='lines', line_color='rgba(256,0,0,0.3)',showlegend=False))
fig.add_scatter(x=pred['Date_updated'],y=pred['predict'], mode="lines",
name='Canada exponential projection',line=dict(width=2, color='red', dash='dot'))
fig.add_scatter(x=pred['Date_updated'],y=pred['Confirmed'], mode="lines+markers+text",
name='Canada',marker=dict(size=5,color='red'),
text=pred['events'],textposition="bottom right",
textfont=dict(family="Ariel",size=14,color='red'))
fig.update_layout(title='Canada COVID-19 Case Growth Curve',title_x=0.5,
titlefont=dict(family='Arial',size=28,color='black'),
plot_bgcolor='white',
xaxis=dict(titlefont=dict(family='Ariel, sans-serif',size=11, color='black'),
showline=True,showgrid=True,showticklabels=True,linecolor='black',
linewidth=2,ticks='outside', mirror=False,
gridcolor="rgba(0,0,0,0.1)",
tickfont=dict(family='Arial',size=15,color='black'),
tickformat= "%d-%b"
),
yaxis=dict(title='Confirmed Case', titlefont=dict(family='Ariel',size=18, color='black'),
showline=True,showgrid=True,showticklabels=True,linecolor='black',
linewidth=2,ticks='outside', mirror=False,
gridcolor="rgba(0,0,0,0.1)",
tickfont=dict(family='Arial',size=15,color='black')
),
legend=dict(x=0.63, y=0.05,
font=dict(family="Ariel",size=16,color="black"), bgcolor='rgba(0,0,0,0)'),
margin = dict(l = 50, r = 50, t = 60, b = 120),
annotations = [dict(x = -0.08, y = -0.25, align='left',
text = captext, showarrow = False, xref='paper', yref='paper',
xanchor='left', yanchor='auto', xshift=0, yshift=0,
font=dict(family='Arial',size=16, color="grey"))]
)
fig.show()
#fig.write_image("output/Canada_growth_"+ dfa.Date_updated.max().strftime('_%m_%d_%Y')+".jpeg", scale=3)
outtable=pred.loc[~pred.predict.isna(),['Date_updated','Confirmed','predict','cf_low','cf_high','Daily Adds', 'predict_adds']].tail(8).copy()
outtable['Date']=outtable['Date_updated'].dt.strftime('%B %d')
outtable['Actual']=[ f"{x:,.0f}" if ~np.isnan(x) else '' for x in outtable['Confirmed'] ]
outtable['Projection']=[f"{x:,.0f}" for x in outtable['predict']]
outtable['Daily Adds']=[f"{x:,.0f}" if ~np.isnan(x) else '' for x in outtable['Daily Adds'] ]
outtable['Projected Adds']=[f"{x:,.0f}" for x in outtable['predict_adds']]
outtable=outtable[['Date','Actual','Projection','Daily Adds','Projected Adds']].set_index('Date').T
display(HTML(outtable.to_html()))
fig = make_subplots(
rows=5, cols=1,
subplot_titles=("Canada","US", "Brazil","Russia","UK"),
vertical_spacing=0.08,
specs=[[{"secondary_y": True}],
[{"secondary_y": True}],
[{"secondary_y": True}],
[{"secondary_y": True}],
[{"secondary_y": True}]])
bw=1000*3600*24
oneday=86400000.0
dd=10
#date_max='2020-08-30'
#Canada
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Canada','Date_updated'],
y=dfa.loc[dfa.Country=='Canada','Confirmed_added'] ,
name='Case',
marker_color='rgb(55, 83, 109)',
width=bw*0.5
),
row=1, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Canada','Date_updated'],
y=dfa.loc[dfa.Country=='Canada','Death_added'] ,
name='Death',
marker_color='blue',
width=bw*0.5
),
row=1, col=1, secondary_y=True)
fig.add_trace(go.Bar(x=pd.to_datetime(['2020-02-26','2020-03-13','2020-03-17','2020-03-25'])
, y=np.repeat(100000,4),name='events', marker_color='rgb(256,0,0)', width=bw*0.2,showlegend=False),
row=1,col=1, secondary_y=False)
fig.add_annotation(x=pd.to_datetime('2020-02-26'), y=2700, ax=140, ay=0, xref='x1',yref='y1',arrowhead=1,
align='left',text="Minister of Health issued warning for COVID-19", font=dict( size=11))
fig.add_annotation(x=pd.to_datetime('2020-03-13'), y=2500, ax=100, ay=0, xref='x1',yref='y1',arrowhead=1,
align='left',text="Recommendation against Intl. travel", font=dict( size=11))
fig.add_annotation(x=pd.to_datetime('2020-03-17'),y=2200, ax=110, ay=0, xref='x1',yref='y1',arrowhead=1,
align='left',text="Travel bans; State of Emergency by Provs", font=dict( size=11))
fig.add_annotation(x=pd.to_datetime('2020-03-25'), y=2000, ax=60, ay=0,xref='x1',yref='y1',arrowhead=1,
align='left',text="Quarantine Act", font=dict( size=11))
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=10*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-03-10',date_max]), row=1, col=1,showgrid=True)
yr=2800
fig.update_yaxes(title_text='Case',range=[0,yr], row=1, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=1, col=1, secondary_y=True,color='blue')
#US
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='US','Date_updated'],
y=dfa.loc[dfa.Country=='US','Confirmed_added'] ,
name='US',
marker_color='rgb(55, 83, 109)',
width=bw*0.5,showlegend=False
),
row=2, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='US','Date_updated'],
y=dfa.loc[dfa.Country=='US','Death_added'] ,
name='US',
marker_color='blue',
width=bw*0.5,showlegend=False
),
row=2, col=1, secondary_y=True)
fig.add_trace(go.Bar(x=pd.to_datetime(['2020-02-29','2020-03-19'])
, y=np.repeat(100000,2),name='events', marker_color='rgb(256,0,0)',width=bw*0.2,showlegend=False),
row=2, col=1, secondary_y=False)
fig.add_annotation(x=pd.to_datetime('2020-02-29'),y=55000, ax=80, ay=0, xref='x2', yref='y3',arrowhead=1,
text="WA: State of Emergency",font=dict( size=11))
fig.add_annotation(x=pd.to_datetime('2020-03-19'), y=60000, ax=100, ay=0, xref='x2', yref='y3',arrowhead=1,
text="More states lockdown; Travel bans",font=dict( size=11))
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=10*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-02-23',date_max]), row=2, col=1,showgrid=True)
yr=80000
fig.update_yaxes(title_text='Case',range=[0,yr], row=2, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=2, col=1, secondary_y=True,color='blue')
#Brazil
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Brazil','Date_updated'],
y=dfa.loc[dfa.Country=='Brazil','Confirmed_added'] ,
name='Brazil',
marker_color='rgb(55, 83, 109)',
width=bw*0.5,showlegend=False
),
row=3, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Brazil','Date_updated'],
y=dfa.loc[dfa.Country=='Brazil','Death_added'] ,
name='Brazil',
marker_color='blue',
width=bw*0.5,showlegend=False
),
row=3, col=1, secondary_y=True)
#fig.add_trace(go.Bar(x=pd.to_datetime(['2020-03-09'])
# , y=np.repeat(100000,1),name='events', marker_color='rgb(256,0,0)',width=bw*0.2,showlegend=False ),
# row=3, col=1, secondary_y=False)
#fig.add_annotation(x=pd.to_datetime('2020-03-10'),y=7000, ax=100,ay=0,xref='x3', yref='y5',arrowhead=1,
# text="Lockdown: Mar 09",font=dict(size=11))
#fig.add_annotation(x=pd.to_datetime('2020-03-22'),y=6500,ax=70,ay=0, xref='x3', yref='y5',arrowhead=1,
# text="Peak: Mar 21",font=dict(size=11))
#fig.add_annotation(x=pd.to_datetime('2020-05-15'),y=6000,ax=0,ay=0, xref='x3', yref='y5',
# text="From lockdown to peak:<br>12 days",font=dict(color="black",size=14))
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=10*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-02-23',date_max]), row=3, col=1,showgrid=True)
yr=100000
fig.update_yaxes(title_text='Case',range=[0,yr], row=3, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=3, col=1, secondary_y=True,color='blue')
#Russia
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Russia','Date_updated'],
y=dfa.loc[dfa.Country=='Russia','Confirmed_added'] ,
name='Russia',
marker_color='rgb(55, 83, 109)',
width=bw*0.5 ,showlegend=False
),
row=4, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Russia','Date_updated'],
y=dfa.loc[dfa.Country=='Russia','Death_added'] ,
name='Russia',
marker_color='blue',
width=bw*0.5 ,showlegend=False
),
row=4, col=1, secondary_y=True)
#fig.add_trace(go.Bar(x=[pd.to_datetime('2020-02-25')]
# , y=[100000], name='events', marker_color='rgb(256,0,0)',width=bw*0.2,showlegend=False),
# row=4, col=1, secondary_y=False)
#fig.add_annotation(
# x=pd.to_datetime('2020-02-26'),y=1000,ax=120,ay=0,xref='x4',yref='y7',arrowhead=1,
# text="Soft lockdown <br> aggressive testing and tracing: <br> Feb 25", font=dict(size=11))
#fig.add_annotation(x=pd.to_datetime('2020-03-04'),y=700,ax=70, ay=0, xref='x4',yref='y7',arrowhead=1,
# text="Peak: Mar 3", font=dict(size=11))
#fig.add_annotation(
# x=pd.to_datetime('2020-05-03'),y=800,ax=0,ay=0,xref='x4',yref='y7',
# text="From aggressive measures to peak: <br> 7 days",font=dict( color="black",size=14))
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=10*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-02-23',date_max]), row=4, col=1,showgrid=True)
yr=12000
fig.update_yaxes(title_text='Case',range=[0,yr], row=4, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=4, col=1, secondary_y=True,color='blue')
#United Kingdom
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='United Kingdom','Date_updated'],
y=dfa.loc[dfa.Country=='United Kingdom','Confirmed_added'] ,
name='United Kingdom',
marker_color='rgb(55, 83, 109)',
width=bw*0.5,showlegend=False
),
row=5, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='United Kingdom','Date_updated'],
y=dfa.loc[dfa.Country=='United Kingdom','Death_added'] ,
name='United Kingdom',
marker_color='blue',
width=bw*0.5,showlegend=False
),
row=5, col=1, secondary_y=True)
#fig.add_trace(go.Bar(x=pd.to_datetime(['2020-01-23','2020-03-25'])
# , y=np.repeat(100000,2),name='events', marker_color='rgb(256,0,0)',width=bw*0.5,showlegend=False),
# row=5,col=1, secondary_y=False)
#fig.add_annotation(
# x=pd.to_datetime('2020-01-23'),y=16000,ax=80,ay=0,xref='x5',yref='y9',arrowhead=1,
# text="Hubei lockdown:<br> Jan 23", font=dict(size=11))
#fig.add_annotation(x=pd.to_datetime('2020-03-25'),y=12000,ax=-70, ay=0, xref='x5',yref='y9',arrowhead=1,
# text="Lockdown Lifted: <br> Mar 25", font=dict(size=11))
#fig.add_annotation(x=pd.to_datetime('2020-02-13'),y=10000,ax=70, ay=0, xref='x5',yref='y9',arrowhead=1,
# text="Peak: Feb 11", font=dict(size=11))
#fig.add_annotation(
# x=pd.to_datetime('2020-05-15'),y=14000,ax=0,ay=0,xref='x5',yref='y9',
# text="Lockdown to peak: <br> 21 days",font=dict( color="black",size=14))
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=10*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-02-23',date_max]), row=5, col=1,showgrid=True)
yr=6000
fig.update_yaxes(title_text='Case',range=[0,yr], row=5, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/4.], row=5, col=1, secondary_y=True,color='blue')
fig.update_layout(height=2500, width=800, showlegend=True, title_text="Daily New Adds",
font=dict(
family="Arial",
size=14,
color="black"
)
)
fig.show()
#fig.write_image("output/daily_newadd_death_"+ dfa.Date_updated.max().strftime('_%m_%d_%Y')+".jpeg", scale=3)
dcan=df[df.Country=='Canada'].copy()
dcan.loc[:,'StartDate']=-999
for j in dcan.Region.unique():
sdate=dcan.loc[(dcan.Region==j) & (dcan.Confirmed > 100),'Date_updated'].min()
##print(f"Country: {j} case number: {dfa[(dfa.Country==j) & (dfa.Confirmed > 100)]['Confirmed'].min()} start date {sdate}")
tmp=dcan.loc[dcan.Region==j,'Date_updated']-sdate
dcan.loc[dcan.Region==j,'StartDate']=tmp.dt.days + 1
#date_max='2020-07-10'
fig = make_subplots(
rows=4, cols=1,
subplot_titles=("Ontario", "Quebec","Alberta","British Columbia"),
vertical_spacing=0.08,
specs=[[{"secondary_y": True}],
[{"secondary_y": True}],
[{"secondary_y": True}],
[{"secondary_y": True}]])
bw=1000*3600*24
oneday=86400000.0
dd=10
##Ontario
ixd=(df.Country=='Canada') & (df.Region =='Ontario')
fig.add_trace(go.Bar(x=df.loc[ixd ,'Date_updated'],
y=df.loc[ixd,'Confirmed_added'] ,
name='Case',
marker_color='rgb(55, 83, 109)',
width=bw*0.5,showlegend=False
),
row=1, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=df.loc[ixd,'Date_updated'],
y=df.loc[ixd,'Death_added'] ,
name='Death',
marker_color='blue',
width=bw*0.5,showlegend=False
),
row=1, col=1, secondary_y=True)
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=dd*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-03-10',date_max]), row=1, col=1,showgrid=True)
yr=800
fig.update_yaxes(title_text='Case',range=[0,yr], row=1, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=1, col=1, secondary_y=True,color='blue')
#Quebec
ixd=(df.Country=='Canada') & (df.Region =='Quebec')
fig.add_trace(go.Bar(x=df.loc[ixd ,'Date_updated'],
y=df.loc[ixd,'Confirmed_added'] ,
name='Case',
marker_color='rgb(55, 83, 109)',
width=bw*0.5 ,showlegend=False
),
row=2, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=df.loc[ixd,'Date_updated'],
y=df.loc[ixd,'Death_added'] ,
name='Death',
marker_color='blue',
width=bw*0.5 ,showlegend=False
),
row=2, col=1, secondary_y=True)
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=dd*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-03-10',date_max]), row=2, col=1,showgrid=True)
yr=2250
fig.update_yaxes(title_text='Case',range=[0,yr], row=2, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/2.], row=2, col=1, secondary_y=True,color='blue')
#Alberta
ixd=(df.Country=='Canada') & (df.Region =='Alberta')
fig.add_trace(go.Bar(x=df.loc[ixd ,'Date_updated'],
y=df.loc[ixd,'Confirmed_added'] ,
name='Case',
marker_color='rgb(55, 83, 109)',
width=bw*0.5 ,showlegend=False
),
row=3, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=df.loc[ixd,'Date_updated'],
y=df.loc[ixd,'Death_added'] ,
name='Death',
marker_color='blue',
width=bw*0.5 ,showlegend=False
),
row=3, col=1, secondary_y=True)
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=dd*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-03-10',date_max]), row=3, col=1,showgrid=True)
yr=800
fig.update_yaxes(title_text='Case',range=[0,yr], row=3, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=3, col=1, secondary_y=True,color='blue')
#British Columbia
ixd=(df.Country=='Canada') & (df.Region =='British Columbia')
fig.add_trace(go.Bar(x=df.loc[ixd ,'Date_updated'],
y=df.loc[ixd,'Confirmed_added'] ,
name='Case',
marker_color='rgb(55, 83, 109)',
width=bw*0.5 ,showlegend=False
),
row=4, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=df.loc[ixd,'Date_updated'],
y=df.loc[ixd,'Death_added'] ,
name='Death',
marker_color='blue',
width=bw*0.5 ,showlegend=False
),
row=4, col=1, secondary_y=True)
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=dd*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-03-10',date_max]), row=4, col=1,showgrid=True)
yr=500
fig.update_yaxes(title_text='Case',range=[0,yr], row=4, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=4, col=1, secondary_y=True,color='blue')
fig.update_layout(height=2000, width=800, showlegend=True, title_text="Daily New Adds",
font=dict(
family="Arial",
size=14,
color="black"
)
)
fig.show()
#fig.write_image("output/daily_newadd_death_Canada_"+ dfa.Date_updated.max().strftime('_%m_%d_%Y')+".jpeg", scale=3)
Three scenarios from CIDRAP 2020
xlong=list(range(0,1000))
xdate_long=[pd.to_datetime('2020-01-22')+np.timedelta64(x,'D') for x in xlong]
sce=pd.DataFrame({'x': xlong, 'date': xdate_long, \
'case': float('NaN'), 'case_city': float('NaN'),\
'case_sm': float('NaN'), 'case_city_sm': float('NaN')}).set_index('date')
#dft_dl=pd.read_excel("https://docs.google.com/uc?export=download&id=1euhrML0rkV_hHF1thiA0G5vSSeZCqxHY",\
# sheet_name="Cases by Reported Date").set_index('Reported Date')
dft_dl=pd.read_excel("D:\Work\Downloads\CityofToronto_COVID-19_Data.xlsx",\
sheet_name="Cases by Reported Date").set_index('Reported Date')
sce.loc[dft_dl.index,'case_city']=dft_dl['Case Count']
sce.loc[(sce.index <= dft_dl.index.max()) & (sce.case_city.isna()),'case_city']=0
sce.loc[dfa.Date_updated[dfa.Country=='Canada'],'case']=dfa.loc[dfa.Country=='Canada','Confirmed_added'].values
sce.loc[(sce.index < dfa.Date_updated[dfa.Country=='Canada'].max()) & (sce.case.isna()),'case']=0
sce['case_city_sm']=sce['case_city'].rolling(5,min_periods=1).mean()
sce['case_sm']=sce['case'].rolling(5,min_periods=1).mean()
sce.loc[sce['case'].isna(),'case_sm']=float("NaN")
sce.loc[sce['case_city'].isna(),'case_city_sm']=float("NaN")
#two gaussian fit for Canada
popt=[96.60058132, 22.35279574, 1639.36962114, 187.78573285, 53.77234086, 415.11806907]
sce['Canada_fit']=gauss_two(sce.x,*popt)
tsep=150
#Peak and Valley
sc=sce['Canada_fit']
for i in range(1,6):
p=[popt[0]+ tsep*i+25, popt[1], popt[2]*0.95**i, popt[3] + tsep*i, popt[4], popt[5]*0.95**i]
sc=sc+gauss_two(sce.x,*p)
sce['sc1 Canada']=sc
sc=sce['Canada_fit']
sc=sc+gauss_one(sce.x, popt[0]+183, popt[1], popt[2]*2.) \
+ gauss_one(sce.x, popt[3]+183, popt[4], popt[5]*0.95)
for i in range(2,5):
p=[popt[0]+ tsep*i, popt[1], popt[2]*0.9**i, popt[3] + tsep*i, popt[4], popt[5]*0.95**i]
sc=sc+gauss_two(sce.x,*p)
sce['sc2 Canada']=sc
sc=sce['Canada_fit']
for i in range(1,10):
p=[popt[3] + tsep/2*i , popt[4]*0.5, popt[5]*0.98**i]
sc=sc+gauss_one(sce.x,*p)
sce['sc3 Canada']=sc
#flu season peak 2021-Jan-26
flu_peak=[370, 33, 700]
sce['Flu']=gauss_one(sce.x,*flu_peak)
sce.reset_index(inplace=True)
fig = make_subplots(rows=1, cols=1, specs=[[{"secondary_y": True}]])
fig.add_trace(go.Scatter(x=sce.date,y=sce['sc1 Canada'], fill='tozeroy'
,mode="none",fillcolor='rgba(173,216,230,0.7)', name='Scenario'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_sm,mode='lines', name='Case - Canada',
line_color='rgb(128, 128, 128)'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_city_sm,mode='lines', name='Case - Toronto' ,
line_color='rgb(0,128,0)'), secondary_y=True)
fig.add_trace(go.Scatter(x=sce.date,y=sce.Flu, mode='lines',name='Regular Flu Season',
line = dict(color='royalblue', width=1, dash='dash')))
fig.update_yaxes(tickvals=[1000,2000,3000,4000,5000], title_text='Canada Daily Adds',range=[0,5000])
fig.update_yaxes(tickvals=np.array([1000,2000,3000,4000,5000])/8, title_text='Toronto Daily Adds',range=[0,5000/8.],
secondary_y=True,color='rgb(0,128,0)')
fig.update_xaxes(title_text='Date',range=['2020-02-01','2022-01-30'])
fig.update_layout(height=500, width=800,
title_text="Scenario 1: Peaks and Valleys",
title_x=0.9,
title_y=0.9,
font=dict(family="Arial, sans-serif",size=16,color="black"),
margin=dict(l=20, r=20, t=20, b=20),
legend=dict(bgcolor='rgba(0,0,0,0)',
yanchor="top",
y=0.8,
xanchor="left",
x=0.6))
fig.show()
fig = make_subplots(rows=1, cols=1, specs=[[{"secondary_y": True}]])
fig.add_trace(go.Scatter(x=sce.date,y=sce['sc2 Canada'], fill='tozeroy'
,mode="none",fillcolor='rgba(173,216,230,0.7)', name='Scenario'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_sm,mode='lines', name='Case - Canada',
line_color='rgb(128, 128, 128)'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_city_sm,mode='lines', name='Case - Toronto' ,
line_color='rgb(0,128,0)'), secondary_y=True)
fig.add_trace(go.Scatter(x=sce.date,y=sce.Flu, mode='lines',name='Regular Flu Season',
line = dict(color='royalblue', width=1, dash='dash')))
fig.update_yaxes(tickvals=[1000,2000,3000,4000,5000], title_text='Canada Daily Adds',range=[0,5000])
fig.update_yaxes(tickvals=np.array([1000,2000,3000,4000,5000])/8, title_text='Toronto Daily Adds',range=[0,5000/8.],
secondary_y=True,color='rgb(0,128,0)')
fig.update_xaxes(title_text='Date',range=['2020-02-01','2022-01-30'])
fig.update_layout(height=500, width=800,
title_text="Scenario 2: Fall Peak",
title_x=0.9,
title_y=0.9,
font=dict(family="Arial, sans-serif",size=16,color="black"),
margin=dict(l=20, r=20, t=20, b=20),
legend=dict(bgcolor='rgba(0,0,0,0)',
yanchor="top",
y=0.8,
xanchor="left",
x=0.6))
fig.show()
fig = make_subplots(rows=1, cols=1, specs=[[{"secondary_y": True}]])
fig.add_trace(go.Scatter(x=sce.date,y=sce['sc3 Canada'], fill='tozeroy'
,mode="none",fillcolor='rgba(173,216,230,0.7)', name='Scenario'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_sm,mode='lines', name='Case - Canada',
line_color='rgb(128, 128, 128)'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_city_sm,mode='lines', name='Case - Toronto' ,
line_color='rgb(0,128,0)'), secondary_y=True)
fig.add_trace(go.Scatter(x=sce.date,y=sce.Flu, mode='lines',name='Regular Flu Season',
line = dict(color='royalblue', width=1, dash='dash')))
fig.update_yaxes(tickvals=[1000,2000,3000,4000,5000], title_text='Canada Daily Adds',range=[0,5000])
fig.update_yaxes(tickvals=np.array([1000,2000,3000,4000,5000])/8, title_text='Toronto Daily Adds',range=[0,5000/8.],
secondary_y=True,color='rgb(0,128,0)')
fig.update_xaxes(title_text='Date',range=['2020-02-01','2022-01-30'])
fig.update_layout(height=500, width=800,
title_text="Scenario 3: Slow Burn",
title_x=0.9,
title_y=0.9,
font=dict(family="Arial, sans-serif",size=16,color="black"),
margin=dict(l=20, r=20, t=20, b=20),
legend=dict(bgcolor='rgba(0,0,0,0)',
yanchor="top",
y=0.8,
xanchor="left",
x=0.6))